package com.vhall.component.service.room.event.handler.live;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.vhall.component.dao.audit.AuditRecordMapper;
import com.vhall.component.dao.room.RecordOfRoomMapper;
import com.vhall.component.dao.room.RoomExtendsMapper;
import com.vhall.component.dao.room.RoomMapper;
import com.vhall.component.dao.room.RoomReservationThirdMapper;
import com.vhall.component.entity.audit.dto.CreateAuditRecordDTO;
import com.vhall.component.entity.audit.entity.AuditRecordEntity;
import com.vhall.component.entity.record.RecordEntity;
import com.vhall.component.entity.room.dto.RoomsEntityDTO;
import com.vhall.component.entity.room.entity.RoomExtendsEntity;
import com.vhall.component.entity.room.entity.RoomReservationThirdEntity;
import com.vhall.component.entity.room.entity.RoomsEntity;
import com.vhall.component.entity.room.enums.RoomAuditStatusEnum;
import com.vhall.component.entity.room.enums.RoomAuditWhichEnum;
import com.vhall.component.entity.room.enums.RoomStatusEnum;
import com.vhall.component.framework.common.constant.CommonConstant;
import com.vhall.component.framework.common.exception.BusinessException;
import com.vhall.component.plugin.common.constant.HttpConstant;
import com.vhall.component.plugin.common.constant.PaasCommonConstant;
import com.vhall.component.plugin.common.constant.RedisKey;
import com.vhall.component.plugin.common.constant.RoomConstant;
import com.vhall.component.plugin.common.exception.BizErrorCode;
import com.vhall.component.plugin.common.support.Token;
import com.vhall.component.plugin.common.support.TokenThreadLocal;
import com.vhall.component.plugin.core.service.PaasService;
import com.vhall.component.service.audit.AuditRecordService;
import com.vhall.component.service.inav.constant.InavConstant;
import com.vhall.component.service.room.RoomInnerService;
import com.vhall.component.service.room.event.handler.AbstractEventHandler;
import com.vhall.component.service.room.event.handler.EventHandlerType;
import com.vhall.component.service.room.event.handler.RedirectUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

import static com.vhall.component.service.room.event.EventConstant.LIVE_STREAM_CHANGE_STATUS;


/**
 * 直播开始停止回调 处理器
 *
 * @author jingwen.li
 * @date 2023/2/16
 */
@Slf4j
@Component
@EventHandlerType(LIVE_STREAM_CHANGE_STATUS)
public class StreamChangeStatusEventHandler extends AbstractEventHandler {
    @Autowired
    private RedirectUtil redirectUtil;
    @Value("${old.callback.live.url}")
    private String callbackUrl;
    @Autowired
    private RoomInnerService roomInnerService;
    @Autowired
    private PaasService paasService;
    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 现在的逻辑是paas只调用这一次的回调，然后在这个回调中去生成回放，这样会导致record服务和room服务发生循环依赖。
     * 所以在这里直接操作record表
     */
    @Autowired
    private RecordOfRoomMapper recordOfRoomMapper;
    @Autowired
    private RoomExtendsMapper roomExtendsMapper;

    @Autowired
    private RoomMapper roomMapper;
    @Resource
    private RoomReservationThirdMapper roomReservationThirdMapper;
    @Resource
    private AuditRecordMapper auditRecordMapper;
    private static DateTimeFormatter DF = DateTimeFormatter.ofPattern(CommonConstant.DATETIME_FORMAT);
    /**
     * 是否开启房间审核
     * 1为开启
     * 0为关闭房间审核
     */
    @Value("${project.room.audit:0}")
    public Integer roomAuditWitch;
    @Override
    public void handle(Map<String, Object> callBack) {
//        redirectUtil.redirect(callbackUrl, callBack);
//        event	string	是	lives/stream-change-status
//        refer	string	是	vhall
//        time	string	是	回调时间戳
//        app_id	string	是	应用ID
//        room_id	string	是	房间ID
//        status	int	是	1 推流中 2 未推流
//        signature	string	是	回调签名
        log.info("收到直播开始停止回调事件：{}, 事件详情：{}", LIVE_STREAM_CHANGE_STATUS, callBack);
        String roomId = (String) callBack.get("room_id");

        RoomsEntityDTO roomsEntityDTO = roomInnerService.getRoomInfo(roomId);
        if (roomsEntityDTO == null) {
            log.error("没有查询到房间");
            return;
        }

        //拉取paas流状态列表
        JSONObject result = paasService.getStreamStatus(roomId);
        if (result.getInteger(HttpConstant.CODE) != HttpConstant.SUCCESS_CODE) {
            log.warn("获取房间={} 流状态失败", roomId);
            return;
        }
        JSONObject data = result.getJSONObject(HttpConstant.DATA);
        JSONObject obj = data.getJSONObject(roomId);
        String pushTime = obj.getString("push_time");
        String endTime = obj.getString("end_time");

        if (obj.getInteger(PaasCommonConstant.STREAM_STATUS) == RoomConstant.STATUS_START) {
            startLive(roomsEntityDTO, pushTime);
            updateReservationRoomStatus(roomsEntityDTO.getIlId(), RoomConstant.STATUS_START);
        } else if (obj.getInteger(PaasCommonConstant.STREAM_STATUS) == RoomConstant.STATUS_STOP) {
            endLive(roomsEntityDTO, pushTime, endTime);
            updateReservationRoomStatus(roomsEntityDTO.getIlId(), RoomConstant.STATUS_STOP);
        }
    }



    private void startLive(RoomsEntityDTO roomsEntityDTO, String pushTime) {
        log.info("房间={} 开始推流", roomsEntityDTO.getRoomId());
        RoomsEntity room = new RoomsEntity();
        // 是否是第三方推流
        boolean thirdPushStream = false;
        if (RoomAuditWhichEnum.OPEN_ROOM_AUDIT.getStatus().equals(roomAuditWitch)
                && !RoomAuditStatusEnum.APPROVED.getAuditStatus().equals(roomsEntityDTO.getAuditStatus())) {
            throw new BusinessException(BizErrorCode.COMP_ROOM_AUDIT_STATUS_ERROR);
        }
        // 如果是主动推流，调用start-live接口(@link RoomFacade)，会主动将房间状态设置成开播状态 STATUS_START 否则就是第三方推流
        if (roomsEntityDTO.getStatus() != RoomConstant.STATUS_START) {
            thirdPushStream = true;
            createRoomExtend(roomsEntityDTO.getRoomId());
            redisTemplate.opsForHash().put(RedisKey.INTERACT_GLOBAL + roomsEntityDTO.getRoomId(), RoomConstant.START_TYPE, "7");
            roomsEntityDTO.setBeginLiveTime(LocalDateTime.parse(pushTime, DF));
        }

        setRoomBeginLiveTime(pushTime, roomsEntityDTO);

        roomsEntityDTO.setStatus(RoomConstant.STATUS_START);
        roomInnerService.updateByIl(roomsEntityDTO);

        // 三方推流 发送消息通知观看端房间已开播
        if (thirdPushStream) {
            JSONObject json = new JSONObject();
            json.put("type", RoomConstant.LIVE_START);
            json.put(InavConstant.ROOM_JOIN_ID, roomsEntityDTO.getAccountId());
            //房间消息
            paasService.sendMessage(roomsEntityDTO.getChannelId(), json.toJSONString());
            //通知消息
            paasService.sendMessage(roomsEntityDTO.getNifyChannel(), json.toJSONString());
        }

        // 开播之后 把对应的账号id 加到 app并发控制级别里面
        redisTemplate.opsForSet().add(RedisKey.LIVING_ROOMS_OF_APPS, roomsEntityDTO.getAccountId());
        //开始直播之后把 当前直播间 id 加入到 账号下的直播房间set   主要 用于做并发控制
        redisTemplate.opsForSet().add(RedisKey.LIVING_ROOMS_OF_ACCOUNT + roomsEntityDTO.getAccountId(), roomsEntityDTO.getIlId());
    }

    private void endLive(RoomsEntityDTO roomsEntityDTO, String pushTime, String endTime) {
        // 直播结束，更新该房间下的，聊天总数
        JSONObject msgCount = paasService.getMessageCount(roomsEntityDTO.getRoomId());
        if (msgCount.getInteger(HttpConstant.CODE) != HttpConstant.SUCCESS_CODE) {
            log.error("获取聊天总数错误");
            return;
        }

        roomInnerService.updateByIl(RoomsEntityDTO.builder()
                .ilId(roomsEntityDTO.getIlId())
                .messageTotal(msgCount.getInteger("count"))
                .endLiveTime(LocalDateTime.parse(endTime, DF)).build());

        /**
         * 如果当前直播的状态已经是停止播放,则说明是手动停止,获取手动开始时的直播时间,用于生成完整回放
         */
        if (RoomConstant.STATUS_STOP == roomsEntityDTO.getStatus()) {
            LocalDateTime beginLiveTime = roomsEntityDTO.getBeginLiveTime();
            if (Objects.nonNull(beginLiveTime)) {
                pushTime = beginLiveTime.format(DF);
            }
            redisTemplate.delete(RedisKey.ROOM_LIVE_START_CHANGE_STATUS_KEY + roomsEntityDTO.getRoomId());
        }
        //直播结束之后 马上 将当前 直播间的连接数清理为0
        redisTemplate.opsForValue().set(RedisKey.CONNECT_COUNT_BY_ROOM + roomsEntityDTO.getIlId(), 0);
        //直播结束之后  将账号下面对应的 房间id  清除 set
        redisTemplate.opsForSet().remove(RedisKey.LIVING_ROOMS_OF_ACCOUNT + roomsEntityDTO.getAccountId(), roomsEntityDTO.getIlId());

        redisTemplate.opsForHash().delete(RedisKey.INTERACT_GLOBAL + roomsEntityDTO.getRoomId(), RoomConstant.START_TYPE);

        // 这个地方目前没有使用，这个字段用来存储直播模式。所以这里不再使用。
        // 创建回放放在更新房间信息后面，为了减少直播结束时间的延迟
        createRecord(roomsEntityDTO, pushTime, endTime);
    }

    private void createRoomExtend(String roomId) {
        RoomExtendsEntity entity = new RoomExtendsEntity();
        entity.setRoomId(roomId);
        LocalDateTime now = LocalDateTime.now();
        entity.setStartTime(now);
        entity.setCreatedAt(now);
        entity.setUpdatedAt(now);
        // 7：第三方推流
        entity.setStartType(7);
        roomExtendsMapper.insert(entity);
    }

    /**
     * 设置房间的开始直播时间
     *
     * @param pushTime       推流时间
     * @param roomsEntityDTO 房间对象
     */
    private void setRoomBeginLiveTime(String pushTime, RoomsEntityDTO roomsEntityDTO) {
        /**
         * 直播间状态的中间态,用于辨别直播间开播时,直播是否首次回调
         * 首次回调时状态为-待直播:RoomConstant.STATUS_WAITING
         */
        Object roomMidStatus = redisTemplate.opsForValue().get(RedisKey.ROOM_LIVE_START_CHANGE_STATUS_KEY + roomsEntityDTO.getRoomId());
        if (Objects.nonNull(roomMidStatus)) {
            int status = Integer.parseInt(roomMidStatus.toString());
            if (status == RoomStatusEnum.TO_BE_BROADCAST.getStatus()) {
                roomsEntityDTO.setBeginLiveTime(LocalDateTime.parse(pushTime, DF));
                redisTemplate.opsForValue().set(RedisKey.ROOM_LIVE_START_CHANGE_STATUS_KEY + roomsEntityDTO.getRoomId(), RoomConstant.STATUS_START, 2, TimeUnit.DAYS);
            }
        } else {
            roomsEntityDTO.setBeginLiveTime(LocalDateTime.parse(pushTime, DF));
            redisTemplate.opsForValue().set(RedisKey.ROOM_LIVE_START_CHANGE_STATUS_KEY + roomsEntityDTO.getRoomId(), RoomConstant.STATUS_START, 2, TimeUnit.DAYS);
        }
    }

    /**
     * 创建 回放
     *
     * @param roomsEntityDTO
     * @param endTime
     */
    private void createRecord(RoomsEntityDTO roomsEntityDTO, String pushTime, String endTime) {
        //2、获取生成信息
        JSONObject vod = paasService.createRecord(roomsEntityDTO.getRoomId(), pushTime, endTime);
        if (vod.getInteger(HttpConstant.CODE) != HttpConstant.SUCCESS_CODE) {
            log.error("添加直播回放生成任务错误,param:[{},{},{}],result:[{}]",
                    roomsEntityDTO.getRoomId(), pushTime, endTime, vod.toJSONString());
        } else {
            String vodId = vod.getJSONObject(HttpConstant.DATA).getString(PaasCommonConstant.VOD_ID_KEY);
            RecordEntity recordEntity = new RecordEntity();
            recordEntity.setRoomId(roomsEntityDTO.getRoomId());
            recordEntity.setAccountId(roomsEntityDTO.getAccountId());
            recordEntity.setIlId(roomsEntityDTO.getIlId());
            recordEntity.setSource(0);
            recordEntity.setVodId(vodId);
            if (roomsEntityDTO.getLiveType() == 2){
                // 假直播房间回放自动通过
                recordEntity.setAuditStatus(RoomAuditStatusEnum.APPROVED.getAuditStatus());
                recordEntity.setAuditStatusSecond(RoomAuditStatusEnum.APPROVED.getAuditStatus());
                RoomsEntity roomsEntity = new RoomsEntity();
                roomsEntity.setIlId(roomsEntityDTO.getIlId());
                roomsEntity.setRecordId(vodId);
                roomsEntity.setPlaybackStatus(RoomAuditStatusEnum.APPROVED.getAuditStatus());
                roomsEntity.setPlaybackStatusSecond(RoomAuditStatusEnum.APPROVED.getAuditStatus());
                roomMapper.updateById(roomsEntity);



            }
            recordEntity.setName(roomsEntityDTO.getRoomId() + " " + pushTime);
            //获取视频详情
            JSONObject recordInfo = paasService.getRecordInfo(vodId);
            if (recordInfo.getInteger(HttpConstant.CODE) != HttpConstant.SUCCESS_CODE) {
                log.error("获取视频详细信息错误:param:[{}], result:[{}]", vodId, recordInfo.toJSONString());
            } else {
                JSONObject vodInfo = recordInfo.getJSONObject(HttpConstant.DATA).getJSONObject("vod_info");
                recordEntity.setCreatedTime(LocalDateTime.parse(vodInfo.getString("created_at"), DF));
                recordEntity.setTranscodeStatus(vodInfo.getInteger("transcode_status"));
                recordEntity.setStorage(vodInfo.getInteger("storage"));
                recordEntity.setDuration(vodInfo.getInteger("duration"));
                recordEntity.setRoomId(vodInfo.getString("room_id"));

                recordEntity.setUpdatedAt(LocalDateTime.now());
                recordOfRoomMapper.insert(recordEntity);

                if (roomsEntityDTO.getLiveType() == 2){
                    AuditRecordEntity auditRecordEntity = new AuditRecordEntity();
                    auditRecordEntity.setAuditTableRecordId(recordEntity.getId().toString());
                    auditRecordEntity.setAuditComments("系统自动审核通过");
                    auditRecordEntity.setAuditStatus(1);
                    auditRecordEntity.setAuditTable("record");
                    LocalDateTime now = LocalDateTime.now();
                    auditRecordEntity.setCreatedAt(now);
                    auditRecordEntity.setUpdatedAt(now);
                    auditRecordEntity.setAccountId(0);
                    auditRecordEntity.setUpdateBy(0);
                    auditRecordMapper.insert(auditRecordEntity);
                }
                log.info("保存回放成功");
            }
        }
    }

    private void updateReservationRoomStatus(Integer ilId, Integer status){
        LambdaQueryWrapper<RoomReservationThirdEntity> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(RoomReservationThirdEntity::getIlId, ilId);
        RoomReservationThirdEntity entity = new RoomReservationThirdEntity();
        entity.setRoomStatus(status);
        roomReservationThirdMapper.update(entity, queryWrapper);
    }
}
